package models;

import play.mvc.*;
import play.libs.*;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import akka.actor.*;
import static akka.pattern.Patterns.ask;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.*;
import models.msgs.*;
import static java.util.concurrent.TimeUnit.*;

public class Table extends UntypedActor {
    
	public static int iloscGraczyLudzi = 1;
	public static int iloscGraczyBotow = 3;
	
	public static int iloscZetonowGracza = 1500;
	public static int wysokoscWpisowego = 100;
	
	private static int iloscGotowychDoGry = 0;
	private static boolean czyGraWystartowala = false;
	private static ActorRef croupier;
	private static int firstPlayer;
	private static int iloscGraczyPoWymianie=0;
	private static boolean afterFirstBetting=false;
	private static boolean allin = false;
	private static boolean afterSecoundBetting = false;
	private static Map<String,Integer> zakladyGraczy;
    // Default table.
    static ActorRef defaultTable = Akka.system().actorOf(Props.create(Table.class));
    private static MyCards[] cards;
    private static int iterator=0;
    private static int iterator2=0;
    // Members of this table.
    static Map<String, ActorRef> members = new HashMap<String, ActorRef>();
    static private Talia deck;
    static Map<String, ActorRef> players;
    static Map<String, ActorRef> playersInThisGame;
    public static void join(final String name, WebSocket.In<JsonNode> in, WebSocket.Out<JsonNode> out ) throws Exception{

        // Send the Join message to the table
        String result = (String)Await.result(ask(defaultTable,new Join(name), 1000), Duration.create(1, SECONDS));
                
        if("OK".equals(result)) {
        	
            ActorRef player =  Akka.system().actorOf(
                    Props.create(Human.class, name, in, out,  defaultTable ));
            
            members.put(name, player);            
            String text =  "my Actor has been added to Table"; 
            notifyAll(new Info(text,name));                             
        }
    }
    
    public static void quit(final String username) throws Exception{

        //to do
    }
	private static int pula;
    
       
    public void onReceive(Object message) throws Exception {
        
        if(message instanceof Join) {
            
            Join join = (Join) message;
                        
           // if(false){
            	// if username is already taken do sth        	
          //  }
          //  else {                
                getSender().tell("OK", getSelf());
         //   }
        } else if (message instanceof Info)
        {   
        	String text = ((Info) message).getText();
        	String name = ((Info) message).getName();  
        	
        	notifyAll(new Info(text, name));
        }  
        else if(message instanceof Quit)  {            
           
            String name = ((Quit)message).getName();            
            members.remove(name);
            players.remove(name);
            playersInThisGame.remove(name);
            String text = "has left the game";            		
            notifyAll(new Info(text, name));
            if(croupier != null)
            	croupier.tell(message, defaultTable);
        } else if(message instanceof Ready) {
        	iloscGotowychDoGry++;
        	String text = "I'm ready";
        	String name = ((Ready)message).getName();
        	notifyAll(new Info(text,name));
        	if(iloscGotowychDoGry == iloscGraczyLudzi) {
        		text = "Let's go";
        		notifyAll(new Info(text,"table"));
        		
        			newGameFirstPart();
        	
        		
        	}
        } else if(message instanceof CardsToExchange) {
        	
	        	CardsToExchange msg = (CardsToExchange) message;
	        if (msg.getQuantity() > 0) {	
	        	Karta[] newCards = getCardsFromDeck(deck, msg.getQuantity());
	        	getSender().tell(new CardsAfterExchange(newCards), defaultTable);

        	}
	        iloscGraczyPoWymianie++;

        	if(iloscGraczyPoWymianie>=playersInThisGame.size() ) {
        		if(!allin)
        			croupier.tell(new FirstBetting(), defaultTable);
        		else
        			if(playersInThisGame.size()>1)
        				collectCards();
        			else{
        				for (ActorRef ac : playersInThisGame.values())
        					ac.tell(new Prize(pula), defaultTable);
        				newGameLastPart(); 
        			
        				nextPart();
        				
        			}
        	}
        }  else if(message instanceof AfterBetting) {
        	AfterBetting ab = (AfterBetting)message;
        	playersInThisGame = ab.getPlayers();
        	allin = ab.getAllin();
        	pula = ab.getPot();
        	zakladyGraczy = ab.getBets();
        	
        	if(playersInThisGame.size()==1) {
				for (ActorRef ac : playersInThisGame.values())
					ac.tell(new Prize(pula), defaultTable);    
				newGameLastPart();
			
				nextPart();
			
        	}
        	if(afterFirstBetting==false){
     
        		exchangeCards();
        		afterFirstBetting=true;
        	} else {
    			if(playersInThisGame.size()>1)
    				collectCards();
    			else {
    				for (ActorRef ac : playersInThisGame.values()) {
    					ac.tell(new Prize(pula), defaultTable);
    				}
    				newGameLastPart();
    			
    				nextPart();
    				
    			}
        	}
        } else if(message instanceof MyCards) {
        	
        	cards[iterator] = (MyCards) message;
        	String tmp = ""; //cards[iterator].getName() + " ";
        	for (int i=0; i<5; i++) {
        		tmp+=cards[iterator].getCards()[i] + " ";
        	}
        	tmp+= SprawdzUklad.jakiUklad(cards[iterator].getCards());
        	notifyAllPlayers(new Info(tmp,cards[iterator].getName()));
        	iterator++;
        	MyCards winner = null;
        	if (iterator == playersInThisGame.size()) {
        		winner = ktoWygral(cards);
        	
	        	if(winner!=null) {
	        		playersInThisGame.get(winner.getName()).tell(new Prize(pula), defaultTable);
	        		newGameLastPart();
	        		nextPart();
	        	} else {
	        		notifyAllPlayers(new Info("Remis","table"));
	        		newGameLastPart();
	        		nextPart();
	        	}
	        	iterator2=0;
        	}
        } if (message instanceof Bankrupt) {
        	playerList[iterator2] = (Bankrupt)message;
        	iterator2++;
        	if(iterator2 == players.size()) {
        		Arrays.sort(playerList);
    
        		notifyAllPlayers(new Info("Player : Cash : Numbers of win","table"));
        		for(int i=playerList.length-1; i>=0;i--) {
        			notifyAllPlayers(new Info(playerList[i].getName()+" : "+playerList[i].getCash()+" : "+playerList[i].getNumberOfWin(),"table"));
        		}
        		for(int i=0; i<playerList.length;i++) {
        			if(playerList[i].getCash()==0) {
        				players.remove(playerList[i].getName());
        			}
        		}
        		iterator2 = 0;
        		if(players.size()>1)
        		newGame();
        		else {
        			notifyAll(new Info("Game over","table"));
        			notifyAll(new GameOver());
        		}

        	}
        } if (message instanceof Options) {
        	Options options = (Options)message;
        	iloscGraczyBotow=options.getQuantityOfBots();
        	iloscGraczyLudzi=options.getQuantityOfHumans();
        	wysokoscWpisowego = options.getBuyIn();
        	iloscZetonowGracza = options.getCash();
        	iloscGotowychDoGry = 0;
        }
        else
        {
            unhandled(message);
        }
        
    }   
    private static void collectCards() { 
		notifyAllPlayersInGame(new ShowMeCards());	
		cards = new MyCards[playersInThisGame.size()];
		
	}
    static private Bankrupt[] playerList;
    static public void nextPart() {
    	notifyAllPlayers(new AreYouBankrupt());
    	playerList = new Bankrupt[players.size()];	
}
	static public void newGame() {
    	

    	deck = new Talia();
    	croupier =  Akka.system().actorOf(
                Props.create(Croupier.class, wysokoscWpisowego, iloscZetonowGracza, players, defaultTable, firstPlayer ));
    	croupier.tell(new CollectBuyIn(), defaultTable);
	   	notifyAllPlayers(new Pot(0));
    	notifyAllPlayers(new LastBet(0));
    	giveCards(players, deck);
    	croupier.tell(new FirstBetting(), defaultTable);
    	
    	
    	czyGraWystartowala=false;
    	iloscGotowychDoGry = 0;

    }
    
    static private void newGameFirstPart() {
    	iloscGraczyPoWymianie=0; 
    	czyGraWystartowala = true;
    	iterator =0;
    	iterator2=0;
    	
    	players = new HashMap<String, ActorRef>();
    	players.putAll(members);
    	players.putAll(createBots());
    	ActorRef cr =  Akka.system().actorOf(
                 Props.create(Croupier.class, wysokoscWpisowego, iloscZetonowGracza, players, defaultTable,0 ));
    	cr.tell(new Cash(), defaultTable);  
    	firstPlayer = (new Random()).nextInt(players.size());	
    	croupier=null;
    	
    	afterFirstBetting=false;
    	allin=false;
    	newGame();
    	
    }
    static private void newGameLastPart() {
    	allin = false;
    	croupier = null;
    	afterFirstBetting=false;
    	playersInThisGame=new HashMap<String, ActorRef>();
    	iloscGraczyPoWymianie=0; 
    	iterator =0;
    	iterator2=0;
    }
    static private Map<String, ActorRef> createBots() {
    	Map<String, ActorRef> bots = new HashMap<String, ActorRef>();
    	for (int i = 0; i < iloscGraczyBotow; i++) {
    		String name = "Bot" + i;
            ActorRef player =  Akka.system().actorOf(
                    Props.create(Bot.class, name, defaultTable ));
            
            bots.put(name, player);            
            String text =  "my Actor has been added to Table"; 
            notifyAll(new Info(text,name));
    	}
    	return bots;
    }
    static private void giveCards(Map<String, ActorRef> players,Talia deck) {
    	for (ActorRef player : players.values()) {
            player.tell(new Cards("table",getCardsFromDeck(deck, 5)), defaultTable);
        }
    }
    static public void notifyAll(Object msg) {
        for (ActorRef member : members.values()) {
            member.tell(msg, defaultTable);
        }
    }   
    static private void exchangeCards() {
    	notifyAllPlayersInGame(new ExchangeCards());
    }
    
    static public void notifyAllPlayers(Object msg) {
        for (ActorRef member : players.values()) {
            member.tell(msg, defaultTable);
        }
    }   
    static public void notifyAllPlayersInGame(Object msg) {
        for (ActorRef member : playersInThisGame.values()) {
            member.tell(msg, defaultTable);
        }
    }   
    static private Karta[] getCardsFromDeck(Talia deck, int quantity) {
    	Karta[] cards = new Karta[quantity];
    	for (int i = 0; i<quantity; i++) {
    		cards[i] = deck.wezKarte();
    	}
    	return cards;
    }
    
    static private int preparePrize(String winnerName) {
    	int prize = 0;
    	int winnerBet = zakladyGraczy.get(winnerName).intValue();
    	for (Integer bet : zakladyGraczy.values()) {
    		if(bet.intValue() > winnerBet) {
    			prize += winnerBet;
    		}
    		else {
    			prize+= bet.intValue();
    		}
    	}
    	return prize;
    }

    static private MyCards ktoWygral(MyCards[] gracze) {
    	MyCards najlepszy = gracze[0];
        
        boolean czyRemis = false;
        for (int i = 1; i < gracze.length; i++) {
            if (SprawdzUklad.wartosciUkladu(gracze[i].getCards()) 
                    > SprawdzUklad.wartosciUkladu(najlepszy.getCards())) {   
                najlepszy = gracze[i];
                czyRemis = false;
            } else {
                if (SprawdzUklad.wartosciUkladu(gracze[i].getCards()) 
                       == SprawdzUklad.wartosciUkladu(najlepszy.getCards())) {
                    int wynik = SprawdzUklad.porownajTeSameUklady(
                           najlepszy.getCards(), gracze[i].getCards(), 
                           SprawdzUklad.wartosciUkladu(najlepszy.getCards()));
                    if (wynik == 0) {
                        czyRemis = true;
                    } else {
                        czyRemis = false;
                        if (wynik < 0) {
                            najlepszy = gracze[i]; 
                        }
                    }       
                }
            }
        }
        if (czyRemis) {
            najlepszy = null;
        }
        return najlepszy; 
    }
}